#include <unistd.h>

#include <iostream>

using namespace std;

// int sum(){
//   int ret = 0;
//   for (int i = 0; i < 50; ++i) {
//     ret += i;
//   }  // 1225
//   return ret;// 函数内部的的 return 为函数返回值 main函数的return为进程退出码
// }

// int main() {
//     //进程终止时
//     OS释放进程申请的相关内核数据和对应的数据代码,本质上就是释放系统资源

//     /*
//         进程终止一般分为三种情况:
//             1.代码跑完结果正确
//             2.代码跑完结果错误
//             3.代码没跑完程序崩溃
//     */

//     int ret = sum();
//     if (ret == 1223)
//       return 0;
//         // main函数返回值的意义主要是进程退出信息(退出码和退出信号)中的退出码
//         // echo $? 打印出最近一次的进程退出码
//     else
//       return 101;

//     /*
//         进程退出码:0 - success(正常退出) 只有一个,非零有无数个
//         不同的退出码代表不同的错误原因
//     */

// }

//-----
/*
    <string.h> or <cstring>  -> char* strerror(int errnum); --
   将退出码转化为字符串输出
*/

// 程序正常退出结果是否正确

// #include <cstring>
// #include <vector>

// int main() {
//   for (int i = 0; i < 150; ++i) {
//     // 打印退出码即对应错误信息
//     printf("strerror(%d) : %s\n", i, strerror(i));
//   }

//   // vector<int> te;
//   // te.resize(10);
//   // te[11] = 10;

//   /*
//       (1)
//           $ls sakfhgaksjhgfksahgka     //ls虽然是命令但本质上还是程序
//           ls: cannot access sakfhgaksjhgfksahgka: No such file or directory
//           $echo $?
//           2

//       (2)
//           vector<int> te;
//           te.resize(10);
//           te[11] = 10;
//           //程序运行......

//           $echo $?
//           134

//       (3)
//             $kill -9 111111111111111111111
//             bash: kill: 111111111111111111111: arguments must be process or
//             job IDs $ echo $? 1     //属于该命令自定义的退出码

//   */
// }

// ----------------------

// int main() {
//     //程序(实际是进程)异常退出或是崩溃时其退出码无意义
//   cout << "hello" << endl;
//   cout << "hello" << endl;
//   cout << "hello" << endl;
//   int *p = nullptr;
//   *p = 'c';
//   cout << "world" << endl;
//   cout << "world" << endl;
//   cout << "world" << endl;
//   return 0;
// }

// ##################################################
// ##################################################

/*
    终止进程:
        retrun 算是正常退出程序    return 后跟进程退出码
        exit();
        _exit();
*/

// int main() {

//   cout << "hello world";
//   // void exit(int status)
//   // void _exit(int status)
//     //其中 status若是为0则正常退出 若是不为0则结束进程

//   exit(111);  // 该函数为封装后(c标准库中的)的函数(调用时将会调用_exit())
//   退出时将会正常的执行清理,刷新缓冲区;   可达到与return一样的效果 _exit(-1);
//   // 系统调用系统接口 退出时直接终止进程 不会执行清理,刷新缓冲区等操作
//   //retrun 语句在main函数中表示终止进程 exit语句在任何位置都表示终止进程
//   /*
//     缓冲区的维护者不为操作系统,若是缓冲区的维护者为操作系统则在进行_exit时将会刷新缓冲区
//     缓冲区的维护者为C标准库
//   */

//   return 0;
// }

// ##################################################
// ##################################################

// 进程等待
/*
  1.子进程退出时若是父进程不管子进程子进程将成为僵尸进程从而导致内存泄漏问题;

  2.父进程创建了子进程,子进程会为父进程完成一些工作任务,子进程完成任务时完成的怎么样?父进程是否需要关心子进程的任务完成情况?
    如果父进程需要关心子进程的任务完成情况应该如何关心?若是不需要关心又该如何处理?

    子进程完成任务无非三种状态:代码跑完结果正确 代码跑完结果错误
  代码可能跑完进程崩溃 此时任何一种情况结束子进程都将使得进程变为僵尸进程
    父进程必须采用进程等待的方式来回收已经变成僵尸进程的子进程
    <sys/types.h> <sys/wait.h>
        pid_t wait(int* status);
        pid_t waitpid(pit_t pid,int* status,int options);

         - status 为输出型参数
            {
              int status = 0;
              waitpid(-1,&status,0);//等待任意子进程直至其状态变为Z状态
  将退出信息
            }
         - waitpid()
  中的pid若是为-1,则父进程等待任意子进程直至状态变化为Z(僵尸状态)
         -            options 参数默认为0,表示阻塞式等待
        wait(NULL) == waitpid(-1,NULL,0);
*/

#include <sys/types.h>
#include <sys/wait.h>

// int main() {
//   //演示 waitpid
//    pid_t id = fork();
//    if(id == 0){
//     //子进程
//     int cnt = 4;
//     while (cnt--) {
//       sleep(1);
//     }
//     exit(0);
//    } else if (id > 0) {
//      // 父进程
//      sleep(6);
//      waitpid(-1, NULL, 0);
//    }
// }

// int main() {
//   // 演示 waitpid
//   pid_t id = fork();
//   if (id == 0) {
//     // 子进程
//     int cnt = 4;
//     while (cnt-- /*正常退出*/) {
//       sleep(1);
//     }
//     int a = 10;
//     a /= 0;
//     exit(110);
//   } else if (id > 0) {
//     // 父进程
//     int status = 0;
//     // sleep(10);
//     waitpid(-1, &status, 0);
//
//     // cout << status<<"  "<<((status >> 7) &1) << endl;
//
//     cout << "----------------------" << endl;
//
//     cout << "退出码:" << ((status >> 8) & 0xFF)
//          << "  终止信号:" << ((status & 0x7F)) << endl;
//
//     cout << "----------------------" << endl;
//
//
//  if(!((status >> 7)&1)){//异常退出
//    cout << "  终止信号:" << ((status & 0x7F)) << endl;
//  } else {
//    cout << "退出码:" << ((status >> 8) & 0xFF) << endl;
//  }

// 由于不是所有的异常退出都会生成对应的core dump文件
// 若是需要使用core dump则需要对core dump进行配置
// 将core
// dump进行配置后则可以根据第八位来判断进程的结束状态从而进行对应的其他处理

/*
  1.全局可否解决使得父进程在运行等待过程中不使用wait\waitpid从而拿到子进程退出结果?
    设置为全局并不能使得父进程获取子进程的异常状态，由于进程间具有独立性,故当全局进行修改时将会发生写时拷贝,
    从而保持进程间的独立性,故不能通过全局变量使父进程获取子进程的借书信息;

  2.进程具有独立性 父进程如何获取子进程的退出信息?wait\waitpid干了什么?
    当子进程结束后,对应的代码和数据将被释放,但是子进程为一个进程,子进程在结束后将会继续维护他的PCB结构体
    内存泄漏的原因主要也是僵尸进程的PCB结构体所占的内存;
    而PCB结构体中存放着进程对应的退出信息;
    wait\waitpid将使父进程去获取子进程的PCB结构体的进程退出信息;

  3.父进程是否有权限获取子进程对应的退出信息？
    父进程本身并没有权限,因为PCB结构体是一个内核数据结构;
    但是wait/waitpid属于系统调用,系统调用的主要操作者为OS,OS有足够权力去干涉自身的内核数据结构;
    包括当父进程使用wait/waitpid去获取子进程的退出信息时是完全具有足够权限的;
*/
//  }
//}

// #####################################

// 阻塞式等待
/*
    //进程阻塞本质上是阻塞在系统函数的内部(还有scanf , cin等)
    //当进程阻塞重新被唤醒后 将从系统函数内部上一次阻塞的位置被唤醒(EIP寄存器)
*/
// int main() {
//   pid_t id = fork();
//   if (id == 0) {
//     // 子进程
//     int cnt = 9;
//     while (cnt) {
//       cout << "子进程 : "
//            << "pid = " << getpid() << "  ppid = " << getppid() << endl;
//       sleep(2);
//     }
//     exit(111);

//   } else if (id > 0) {
//     // 父进程
//     int status = 0;
//     pid_t id = waitpid(-1, &status, 0);
//
//
/*
   waitpid  的3个返回值,
   > 0 ,表示等待成功并且返回对应的子进程的pid
   < 0 ,表示等待失败
   == 0 , 表示函数调用成功但是当前并未存在已经结束(死亡)的子进程
*/
//
//     // printf(
//     //     "阻塞式等待等待成功，其中等待的进程PID为: %d ,子进程退出码为: %d "
//     //     ",子进程退出信号状态为: %d \n",
//     //     id, status >> 8 & 0xff, status & 0x7f);
//     //该方法可以提取出子进程对应的退出信息 但是需要进行位运算 不是很推荐

//     if(WIFEXITED(status)){//条件为真时
//       //子进程正常退出
//       printf("子进程执行完毕,退出码:%d\n", WEXITSTATUS(status));
//     }
//     else{//当WIFEXITED为0(假)时,表示子进程异常退出
//       printf("子进程异常退出 : %d\n", WIFEXITED(status));
//     }

//   } else {
//     // 创建子进程失败
//     exit(-1);
//   }

//   return 0;
// }

// #####################################

// // 非阻塞式等待

// int main() {
//   pid_t id = fork();
//   if (id == 0) {
//     // 子进程
//     int cnt = 5;
//     while (cnt--) {
//       cout << "子进程 : "
//            << "pid = " << getpid() << "  ppid = " << getppid() << endl;
//       sleep(1);
//     }
//     exit(111);
//   } else if (id > 0) {
//     // 父进程
//     bool quite = false;
//     while (!quite) {
//       cout << "父进程 : "
//            << "pid = " << getpid() << "  ppid = " << getppid() << endl;
//       sleep(1);
//       int status = 0;
//       pid_t res = waitpid(-1, &status, WNOHANG);
//       if(res>0){
//         printf("等待成功 进程执行完毕,退出码:%d\n", WEXITSTATUS(status));
//         quite = true;
//       }
//       else if (res == 0)
//         (cout << "不存在已经结束的子进程" << endl);
//       else{
//         perror("子进程等待失败");
//         quite = true;
//       }
//     }
//   } else {
//     // 创建子进程失败
//   }

//   return 0;
// }

// #####################################

// 非阻塞式等待时父进程进行其他操作

// #include <vector>
// typedef void (*FunPoint)();
// vector<FunPoint> P_FunV;

// void Func1() { cout << "Func1()" << endl; }
// void Func2() { cout << "Func2()" << endl; }

// void Load() {
//   P_FunV.push_back(Func1);
//   P_FunV.push_back(Func2);
// }

// int main() {
//   pid_t id = fork();
//   if (id == 0) {
//     // 子进程
//     int cnt = 5;
//     while (cnt--) {
//       cout << "子进程 : "
//            << "pid = " << getpid() << "  ppid = " << getppid() << endl;
//       sleep(1);
//     }
//     exit(111);
//   } else if (id > 0) {
//     // 父进程
//     bool quite = false;
//     while (!quite) {
//       cout << "父进程 : "
//            << "pid = " << getpid() << "  ppid = " << getppid() << endl;
//       sleep(1);
//       int status = 0;
//       pid_t res = waitpid(-1, &status, WNOHANG);
//       if (res > 0) {
//         printf("等待成功 进程执行完毕,退出码:%d\n", WEXITSTATUS(status));
//         quite = true;
//       } else if (res == 0){
//         //在该条件当中 父进程并未识别到已经结束的任意子进程
//         //且在等待时进行的是非阻塞式等待,故父进程可以在等待期间通过循环参与其他工作
//         (cout << "不存在已经结束的子进程" << endl);
//         if(P_FunV.empty())
//           Load();
//           else{
//             for(auto iter:P_FunV){
//               iter();
//             }
//           }
//       } else {
//         perror("子进程等待失败");
//         quite = true;
//       }
//     }
//   } else {
//     // 创建子进程失败
//   }

//   return 0;
// }

// #####################################
// #include <cstring>
// int main() {
//   for (int i = 0; i < 150; ++i) {
//     printf("strerror(%d) : %s \n", i, strerror(i));
//   }
//   return 111;
// }

// #####################################

// #include<cstdlib>
// #include<unistd.h>
// #include <cstring>

// int main() {
//   cout << "hello world";
//   _exit(-1);
// }

// #include <sys/types.h>
// #include <sys/wait.h>

// int main() {
//   pid_t id = fork();
//   if (id == 0) {
//     //  子进程
//     int cnt = 2;
//     while (cnt--) {
//       cout << "pid : " << getpid() << "  ppid : " << getppid() << endl;
//       sleep(1);
//     }
//   } else if (id > 0) {
//     //  父进程
//     sleep(4);
//     wait(NULL);
//   } else {
//     // 创建进程失败
//     exit(-1);
//   }
//   return 0;
// }


// //-----------------------

// #include <sys/types.h>
// #include <sys/wait.h>

// int main() {
//   pid_t id = fork();
//   if (id == 0) {
//     //  子进程
//     int cnt = 2;
//     while (cnt--) {
//       cout << "pid : " << getpid() << "  ppid : " << getppid() << endl;
//       sleep(1);
//     }
//     exit(111);
//   } else if (id > 0) {
//     //  父进程
//     sleep(4);
//     int status = 0;
//     pid_t id = waitpid(-1,&status,0);
//     printf("pid = %d , status = %d \n", id, status);
//   } else {
//     // 创建进程失败
//     exit(-1);
//   }
//   return 0;
// }

// //-----------------------

// #include <sys/types.h>
// #include <sys/wait.h>

// int main() {
//   pid_t id = fork();
//   if (id == 0) {
//     //  子进程
//     int cnt = 2;
//     while (cnt--) {
//       cout << "pid : " << getpid() << "  ppid : " << getppid() << endl;
//       sleep(1);
//     }
//     exit(111);
//   } else if (id > 0) {
//     //  父进程
//     sleep(4);
//     int status = 0;
//     pid_t id = waitpid(-1, &status, 0);
//     printf("pid = %d , 退出码 = %d \n", id, (status>>8)&0xff);
//   } else {
//     // 创建进程失败
//     exit(-1);
//   }
//   return 0;
// }

//-----------------------

// #include <sys/types.h>
// #include <sys/wait.h>

// int main() {
//   pid_t id = fork();
//   if (id == 0) {
//     //  子进程
//     int cnt = 2;
//     while (cnt) {
//       cout << "pid : " << getpid() << "  ppid : " << getppid() << endl;
//       sleep(1);
//     }
//     exit(111);
//   } else if (id > 0) {
//     //  父进程
//     sleep(4);
//     int status = 0;
//     pid_t id = waitpid(-1, &status, 0);
//     printf("pid = %d , 信号状态 = %d \n", id, status& 0x7f);
//   } else {
//     // 创建进程失败
//     exit(-1);
//   }
//   return 0;
// }

//-----------------------

// #include <sys/types.h>
// #include <sys/wait.h>

// int main() {
//   pid_t id = fork();
//   if (id == 0) {
//     //  子进程
//     int cnt = 2;
//     while (cnt--) {
//       cout << "pid : " << getpid() << "  ppid : " << getppid() << endl;
//       sleep(1);
//     }
//     exit(111);
//   } else if (id > 0) {
//     //  父进程
//     sleep(4);
//     int status = 0;
//     waitpid(-1, &status, 0);
//     if(WIFEXITED(status)){  //利用WIFEXITESD判断进程是否正常退出
//       printf("进程退出码为: %d\n", WEXITSTATUS(status));//  利用WEXITSTATUS获取进程退出信息中的退出码
//     }
//     else{ 
//       cout << "进程异常终止" << endl;
//     }

//   } else {
//     // 创建进程失败
//     exit(-1);
//   }
//   return 0;
// }



